/*
 * File for generic functions, such as rounding, statistics, etc.
 */
package ece496.pkg496_spu;

import java.util.Collections;
import java.util.ArrayList;

/**
 *
 * @author Yiwen Su
 */
public class math_utils {

    /*====================Round to 1 decimal places (START)===================*/
    public double round_1(double value) {
        double rounded = Math.floor(value * 10 + 0.5) / 10;
        return (rounded);
    }
    /*=====================Round to 1 decimal places (END)====================*/

    /*====================Round to 2 decimal places (START)===================*/
    public double round_2(double value) {
        double rounded = Math.floor(value * 100 + 0.5) / 100;
        return (rounded);
    }
    /*=====================Round to 2 decimal places (END)====================*/

    /*====================Round to 3 decimal places (START)===================*/
    public double round_3(double value) {
        double rounded = Math.floor(value * 1000 + 0.5) / 1000;
        return (rounded);
    }
    /*=====================Round to 3 decimal places (END)====================*/

    /*=====================Mean of a sample array (START)=====================*/
    public double mean(double[] sample) {
        double sum = 0;
        for (int i = 0; i < sample.length; i++) {
            sum = sum + sample[i];
        }
        double mean = sum / sample.length;
        return mean;
    }
    /*======================Mean of a sample array (END)======================*/

    /*====================Median of a sample array (START)====================*/
    public double median(double[] sample) {
        ArrayList<Double> list = CreateDoubleList(sample);
        Collections.sort(list);

        double result;

        if (list.size() % 2 == 1) {
            result = list.get((int) Math.floor(list.size() / 2));
        } else {
            double lower = list.get(list.size() / 2 - 1);
            double upper = list.get(list.size() / 2);

            result = (lower + upper) / 2.0;
        }

        return result;
    }
    /*=====================Median of a sample array (END)=====================*/

    /*==============Create a Java "List" out of an array (START)==============*/
    //Used for Collections.sort function
    private static ArrayList CreateDoubleList(double[] values) {
        ArrayList list = new ArrayList();

        for (int i = 0; i < values.length; i++) {
            list.add(values[i]);
        }

        return list;
    }
    /*===============Create a Java "List" out of an array (END)===============*/

    /*=====================Mode of a sample array (START)=====================*/
    //Not necessary at the moment (Yiwen on Nov 27, 2011)
    /*======================Mode of a sample array (END)======================*/
    //
    /*==============Standard Deviation of a sample array (START)==============*/
    public double std_dev(double[] sample, double mean) {
        double sigma;
        double variance;
        double spread_sum_sq = 0;

        for (int i = 0; i < sample.length; i++) {
            spread_sum_sq += Math.pow(sample[i] - mean, 2.0);
        }

        variance = spread_sum_sq / (sample.length - 1.0);

        sigma = Math.sqrt(variance);
        return sigma;
    }
    /*===============Standard Deviation of a sample array (END)===============*/

    /*======================Outliers discarding (START)=======================*/
    //Outlier filtering based on: http://www.wikihow.com/Reject-Outliers-in-Data
    public double sample_filter(double[] sample) {
        double mean = mean(sample);
        double sigma = std_dev(sample, mean);

        if (sigma != 0) {
            for (int i = 0; i < sample.length; i++) {
                double normal = normDist(sample[i], mean, sigma);

                if (normal < 0.05) {
                    sample[i] = mean;
                    
//This algorithm can be improved
//                    if (i == 0) {
//                        sample[i] = (sample[i + 1] + mean) / 2.0;
//                    } else if (i == sample.length - 1) {
//                        sample[i] = (sample[i - 1] + mean) / 2.0;
//                    } else {
//                        sample[i] = ((sample[i - 1] + mean) / 2.0 + (sample[i + 1] + mean) / 2.0) / 2.0;
//                    }
                }
            }
        }

        return median(sample);
        //Uses Normal distribution to check for discarding conditions
        //Basically, if any sample[i]'s normDist < 0.05, process this outlier
    }
    /*=======================Outliers discarding (END)========================*/

    /*======================Normal distribution (START)=======================*/
    //Used by Outliers discarding
    //Code is based on: http://www.alina.ch/oliver/faq-excel-normdist.shtml
    private static double normDist(double X, double mean, double sigma) {
        double norm = 0;

        final double z = (X - mean) / sigma;

        if (z == 0) {
            return 0.5;
        } else {
            final double oor2pi = 1 / (Math.sqrt(2 * Math.PI));
            double t = 1 / (1 + 0.2316419 * Math.abs(z));
            t *= oor2pi * Math.exp(-0.5 * z * z) * (0.31938153 + t * (-0.356563782 + t * (1.781477937 + t * (-1.821255978 + t * 1.330274429))));

            if (z >= 0) {
                norm = 1 - t;
                return (1 - norm);
            } else {
                norm = t;
                return norm;
            }
        }
    }
    /*=======================Normal distribution (END)========================*/
}
